package app

import (
	"APT/internal/consts"
	"APT/internal/dao"
	"APT/internal/library/cache"
	"APT/internal/library/hgorm/hook"
	"APT/internal/model"
	"APT/internal/model/entity"
	"APT/internal/model/input/input_hotel"
	"APT/internal/model/input/input_refund"
	"APT/internal/service"
	"APT/utility/convert"
	"APT/utility/rabbitmq"
	"APT/utility/uuid"
	"context"
	"database/sql"
	"errors"
	"fmt"
	"github.com/gogf/gf/v2/container/gvar"
	"github.com/gogf/gf/v2/database/gdb"
	"github.com/gogf/gf/v2/frame/g"
	"github.com/gogf/gf/v2/os/gtime"
	"github.com/shopspring/decimal"
	"math"
	"time"

	"github.com/gogf/gf/v2/errors/gcode"
	"github.com/gogf/gf/v2/errors/gerror"

	"APT/api/app/hotel"
)

func (c *ControllerHotel) OrderChangeOptions(ctx context.Context, req *hotel.OrderChangeOptionsReq) (res *hotel.OrderChangeOptionsRes, err error) {
	var (
		PreRefundOut *input_hotel.PreRefundOut
	)
	res = new(hotel.OrderChangeOptionsRes)
	if err = dao.PmsAppReservation.Ctx(ctx).WherePri(req.OrderId).WithAll().Scan(&res); err != nil {
		return
	}

	// 如果订单状态为已取消那么不可变更订单
	if res.OrderStatus == "CANCEL" {
		return
	}

	if res.CheckinStatus == "before_checkin" {
		// 可以进行变更
		res.IsNextChange = true
		// 可以取消订单
		res.IsCancelOrder = true
	}

	if res.CheckinStatus != "checked_out" {
		// 可以续住
		res.IsStayOn = true
	}

	// 该入住单是否在免费取消内
	PreRefundOut, err = service.HotelService().PreRefundOrderDetail(ctx, &input_hotel.PreRefundIn{OrderSn: res.OrderSn})
	if err != nil {
		err = nil
		res.IsNextDateChange = false
	} else {
		if PreRefundOut.CancelFee == 0 {
			res.IsNextDateChange = true
		}
	}

	return
}

func (c *ControllerHotel) OrderChangeGuestEdit(ctx context.Context, req *hotel.OrderChangeGuestEditReq) (res *hotel.OrderChangeGuestEditRes, err error) {
	if err = service.HotelService().OrderChangeGuestEdit(ctx, &req.OrderChangeGuestReq); err != nil {
		return
	}
	return
}

func (c *ControllerHotel) OrderChangeGuestInfo(ctx context.Context, req *hotel.OrderChangeGuestInfoReq) (res *hotel.OrderChangeGuestInfoRes, err error) {
	var (
		appReservation  *entity.PmsAppReservation
		PmsGuestProfile *entity.PmsGuestProfile
	)
	res = new(hotel.OrderChangeGuestInfoRes)
	if err = dao.PmsAppReservation.Ctx(ctx).WherePri(req.OrderId).Scan(&appReservation); err != nil {
		return
	}
	if g.IsEmpty(appReservation) {
		err = gerror.New("不存在入住单")
		return
	}
	if err = dao.PmsGuestProfile.Ctx(ctx).Where(g.MapStrAny{
		dao.PmsGuestProfile.Columns().Uid: appReservation.MainGuest,
	}).Scan(&PmsGuestProfile); err != nil {
		return
	}
	if !g.IsEmpty(PmsGuestProfile) {
		res.Phone = PmsGuestProfile.Phone
		res.Email = PmsGuestProfile.Email
		res.FirstName = PmsGuestProfile.FirstName
		res.LastName = PmsGuestProfile.LastName
		res.AreaNo = PmsGuestProfile.AreaNo
	}
	res.AirhostOrderUuid = appReservation.Uuid
	res.OrderId = appReservation.Id
	return
}

func (c *ControllerHotel) OrderChangeDatePreInfo(ctx context.Context, req *hotel.OrderChangeDatePreInfoReq) (res *hotel.OrderChangeDatePreInfoRes, err error) {
	var (
		appReservation *entity.PmsAppReservation
		PmsRoomType    *entity.PmsRoomType
		Availabilities []*entity.PmsAvailabilities
		Charge         []*entity.PmsCharge
		NewAllAmount   float64
		CancelRate     []*entity.PmsCancelRate
		ChangeOrderSn  = uuid.CreateOrderCode("HC")
		PayConfig      *model.PayConfig
		ExpirationTime int
		PmsPirceConfig *model.PmsPriceConfig
	)
	if PmsPirceConfig, err = service.BasicsConfig().GetPmsPrice(ctx); err != nil {
		return
	}
	PricePercent := decimal.NewFromInt(PmsPirceConfig.PricePercent).Div(decimal.NewFromInt(100)).Add(decimal.NewFromInt(1))
	res = new(hotel.OrderChangeDatePreInfoRes)
	// 查询订单信息
	if err = dao.PmsAppReservation.Ctx(ctx).Where(g.Map{
		dao.PmsAppReservation.Columns().Id: req.OrderId,
	}).Scan(&appReservation); err != nil {
		return
	}

	if g.IsEmpty(appReservation) {
		err = gerror.New("不存在入住单")
		return
	}

	// 查询房间名称
	if err = dao.PmsRoomType.Ctx(ctx).Where(g.Map{
		dao.PmsRoomType.Columns().Uid: appReservation.RoomType,
	}).Hook(hook.PmsFindLanguageValueHook).Scan(&PmsRoomType); err != nil {
		return
	}

	if g.IsEmpty(PmsRoomType) {
		err = gerror.New("房间不存在")
		return
	}

	res.RoomName = PmsRoomType.Name
	res.RoomNumber = 1

	// 查询变更单是否存在
	if err = service.HotelService().OrderChangeCheckIsExist(ctx, req.OrderId, "DATE"); err != nil {
		return
	}
	res.OldCheckInDate = appReservation.CheckinDate.Format("Y-m-d")
	res.OldCheckOutDate = appReservation.CheckoutDate.Format("Y-m-d")
	res.NewCheckInDate = req.CheckInDate
	res.NewCheckOutDate = req.CheckOutDate

	// 查询历史订单价格
	if err = dao.PmsCharge.Ctx(ctx).Where(g.Map{
		dao.PmsCharge.Columns().Uid: appReservation.Charges,
	}).Scan(&Charge); err != nil {
		return
	}

	if g.IsEmpty(Charge) {
		err = gerror.New("不存在价格信息")
		return
	}

	// 查询价格
	Availabilities = nil
	if err = dao.PmsAvailabilities.Ctx(ctx).
		Where(dao.PmsAvailabilities.Columns().Tuid, appReservation.RoomType).
		WhereGTE(dao.PmsAvailabilities.Columns().Date, res.NewCheckInDate).
		WhereLT(dao.PmsAvailabilities.Columns().Date, res.NewCheckOutDate).
		Scan(&Availabilities); err != nil {
		return
	}

	// 读取支付配置
	if PayConfig, err = service.BasicsConfig().GetPay(ctx); err != nil {
		return
	}
	ExpirationTime = gvar.New(gtime.Now().Unix() + PayConfig.HotelStayExp).Int()
	for _, v := range Availabilities {
		if v.Allotment < 1 {
			err = gerror.NewCode(gcode.New(1111, "您选择的日期已经没有满足条件的房间库存了", nil))
			return
		}

		price := decimal.NewFromFloat(v.Price).Mul(PricePercent).Round(0).InexactFloat64()
		//price := v.Price
		for _, v1 := range Charge {
			if v.Date == v1.Date.Format("Y-m-d") {
				price = v1.Amount
			}
		}
		NewAllAmount += price
	}

	res.NewOrderPrice = NewAllAmount
	res.OldOrderPrice = appReservation.BookingFee
	DiffOrderPrice := decimal.NewFromFloat(res.NewOrderPrice).Sub(decimal.NewFromFloat(appReservation.BookingFee)).Round(0).InexactFloat64()
	res.DiffOrderPrice = DiffOrderPrice
	if err = g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) (err error) {
		if err = cache.Instance().Set(ctx, fmt.Sprintf("changeOrder:%s", ChangeOrderSn), &entity.PmsAppReservationChange{
			ChangeOrderSn:   ChangeOrderSn,
			OrderId:         appReservation.Id,
			OrderSn:         appReservation.OrderSn,
			OutOrderSn:      appReservation.OutOrderSn,
			OldCheckinDate:  appReservation.CheckinDate,
			OldCheckoutDate: appReservation.CheckoutDate,
			NewCheckinDate:  gtime.New(req.CheckInDate),
			NewCheckoutDate: gtime.New(req.CheckOutDate),
			ChangeType:      "DATE",
			ChangeStatus:    "ING",
			ChangeAmount:    DiffOrderPrice,
			SubmitDate:      gtime.Now(),
			PricePercent:    PmsPirceConfig.PricePercent,
			ExpirationTime:  ExpirationTime}, time.Second*60*15); err != nil {
			return
		}
		return
	}); err != nil {
		return
	}
	res.ChangeOrderSn = ChangeOrderSn
	if err = dao.PmsCancelRate.Ctx(ctx).Hook(hook.PmsFindLanguageValueHook).OrderAsc(dao.PmsCancelRate.Columns().Sort).Scan(&CancelRate); err != nil && errors.Is(err, sql.ErrNoRows) {
		return
	}
	for _, v := range CancelRate {
		NowTime := gtime.Now().Format("Y-m-d")
		switch v.Mode {
		case "after":
			startDate := gtime.New(req.CheckInDate).Add(time.Duration(-v.StartDays*24) * time.Hour).Format("Y-m-d")
			//res.CancelDate = append(res.CancelDate, fmt.Sprintf("%s%s", startDate, v.Name))
			if len(res.CancelDate) >= 1 {
				res.CancelDate = append(res.CancelDate, fmt.Sprintf("%s%s", startDate, v.Name))
			} else {
				res.CancelDate = append(res.CancelDate, fmt.Sprintf("%s", v.Name))
			}

			break
		case "middle":
			startDay := gtime.New(req.CheckInDate).Add(time.Duration(-v.StartDays*24) * time.Hour).Format("Y-m-d")
			endDate := gtime.New(req.CheckInDate).Add(time.Duration(-v.EndDays*24) * time.Hour).Format("Y-m-d")
			if NowTime <= endDate {
				res.CancelDate = append(res.CancelDate, fmt.Sprintf("%s %s", fmt.Sprintf("%s ~ %s", startDay, endDate), v.Name))
			}
			break
		case "before":
			endDate := gtime.New(req.CheckInDate).Add(time.Duration(-v.EndDays*24) * time.Hour).Format("Y-m-d")
			if endDate >= NowTime {
				res.CancelDate = append(res.CancelDate, fmt.Sprintf("%s %s", endDate, v.Name))
			}
			break
		}
	}
	return
}

func (c *ControllerHotel) OrderChangeBookingPeople(ctx context.Context, req *hotel.OrderChangeBookingPeopleReq) (res *hotel.OrderChangeBookingPeopleRes, err error) {
	var (
		AppReservation   *entity.PmsAppReservation
		RoomType         *entity.PmsRoomType
		ChangeOrderSn    = uuid.CreateOrderCode("HC")
		OldAllNum        int
		OldAllNumPrice   float64
		NewAllNum        int
		NewAllNumPrice   float64
		ChangeSubmitDate = gtime.Now()
		ExpirationTime   int
		PayConfig        *model.PayConfig
	)
	// 查询变更单是否存在
	if err = service.HotelService().OrderChangeCheckIsExist(ctx, req.OrderId, "PEOPLE"); err != nil {
		return
	}
	res = new(hotel.OrderChangeBookingPeopleRes)
	if err = dao.PmsAppReservation.Ctx(ctx).WherePri(req.OrderId).WithAll().Scan(&AppReservation); err != nil {
		return
	}
	if g.IsEmpty(AppReservation) {
		err = gerror.New("不存在入住单")
		return
	}
	if err = dao.PmsRoomType.Ctx(ctx).
		Where(dao.PmsRoomType.Columns().Uid, AppReservation.RoomType).
		Scan(&RoomType); err != nil {
		return
	}
	if g.IsEmpty(RoomType) {
		err = gerror.New("该房型异常")
		return
	}
	if RoomType.Occupancy < req.Adult+req.Child+req.Infant {
		err = gerror.New("入住人数不符合要求")
		return
	}
	OldAllNum = AppReservation.AdultCount + AppReservation.ChildCount
	OldAllNumPrice = decimal.NewFromInt(gvar.New(OldAllNum).Int64()).
		Sub(decimal.NewFromInt(gvar.New(RoomType.OccupantsForBaseRate).Int64())).
		Mul(decimal.NewFromFloat(RoomType.AdditionalGuestAmounts)).
		Round(0).InexactFloat64()
	if OldAllNumPrice < 0 {
		OldAllNumPrice = 0
	}
	NewAllNum = req.Adult + req.Child
	NewAllNumPrice = decimal.NewFromInt(gvar.New(NewAllNum).Int64()).
		Sub(decimal.NewFromInt(gvar.New(RoomType.OccupantsForBaseRate).Int64())).
		Mul(decimal.NewFromFloat(RoomType.AdditionalGuestAmounts)).
		Round(0).InexactFloat64()
	if NewAllNumPrice < 0 {
		NewAllNumPrice = 0
	}
	// 根据OldAllNum重新计算价格
	if NewAllNumPrice > OldAllNumPrice {
		res.ChangePrice = decimal.NewFromFloat(NewAllNumPrice).
			Sub(decimal.NewFromFloat(OldAllNumPrice)).
			Round(0).InexactFloat64()
	}
	res.OldOrderPrice = AppReservation.BookingFee
	res.NewOrderPrice = AppReservation.BookingFee + res.ChangePrice
	if err = g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) (err error) {

		// 读取支付配置
		if PayConfig, err = service.BasicsConfig().GetPay(ctx); err != nil {
			return
		}
		ExpirationTime = gvar.New(gtime.Now().Unix() + PayConfig.HotelStayExp).Int()

		// 插入变更单
		if _, err = dao.PmsAppReservationChange.Ctx(ctx).TX(tx).Data(&entity.PmsAppReservationChange{
			OrderId:        AppReservation.Id,
			OrderSn:        AppReservation.OrderSn,
			OutOrderSn:     AppReservation.OutOrderSn,
			ChangeType:     "PEOPLE",
			OldAdultCount:  AppReservation.AdultCount,
			NewAdultCount:  req.Adult,
			OldChildCount:  AppReservation.ChildCount,
			NewChildCount:  req.Child,
			OldInfantCount: AppReservation.InfantCount,
			NewInfantCount: req.Infant,
			ChangeStatus:   "ING",
			ChangeAmount:   res.ChangePrice,
			ChangeOrderSn:  ChangeOrderSn,
			SubmitDate:     ChangeSubmitDate,
			OldOrderPrice:  res.OldOrderPrice,
			NewOrderPrice:  res.NewOrderPrice,
			ExpirationTime: ExpirationTime,
		}).InsertAndGetId(); err != nil {
			return
		}

		return
	}); err != nil {
		return
	}
	res.ChangeOrderSn = ChangeOrderSn
	return
}

func (c *ControllerHotel) OrderChangeBookingPeopleSubmit(ctx context.Context, req *hotel.OrderChangeBookingPeopleSubmitReq) (res *hotel.OrderChangeBookingPeopleSubmitRes, err error) {
	var (
		PmsAppReservationChange *entity.PmsAppReservationChange
		TransactionSn           = uuid.CreatePayCode("PT")
		PayConfig               *model.PayConfig
		ExpirationTime          int
	)
	if PayConfig, err = service.BasicsConfig().GetPay(ctx); err != nil {
		return
	}
	ExpirationTime = gvar.New(gtime.Now().Unix() + PayConfig.HotelStayExp).Int()
	res = new(hotel.OrderChangeBookingPeopleSubmitRes)
	if err = dao.PmsAppReservationChange.Ctx(ctx).Where(g.MapStrAny{
		dao.PmsAppReservationChange.Columns().ChangeOrderSn: req.ChangeOrderSn,
	}).Scan(&PmsAppReservationChange); err != nil {
		return
	}
	if PmsAppReservationChange.ChangeStatus != "ING" {
		err = gerror.New("该变更单状态异常")
		return
	}
	if err = g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) (err error) {
		if PmsAppReservationChange.ChangeAmount > 0 {
			// 插入支付订单流水
			if _, err = dao.PmsTransaction.Ctx(ctx).TX(tx).OmitEmptyData().Data(&entity.PmsTransaction{
				OrderSn:       PmsAppReservationChange.OrderSn,
				ChangeOrderSn: req.ChangeOrderSn,
				TransactionSn: TransactionSn,
				PayChannel:    "PAYCLOUD",
				PayType:       "WeChatPay",
				OrderType:     "BOOKING_CHANGE",
				Scene:         "HOTEL",
				Amount:        PmsAppReservationChange.ChangeAmount,
				PriceCurrency: "JPY",
				PayStatus:     "WAIT",
				ExpiredTime:   gtime.New(ExpirationTime),
			}).InsertAndGetId(); err != nil {
				return
			}
			if err = rabbitmq.SendMqMessage(ctx, &rabbitmq.SendMqMessageParams{
				ExchangeName: consts.RabbitMQExchangeDelayedName,
				QueueName:    consts.RabbitMQQueueNameOrderExpire,
				DataByte:     gvar.New(req.ChangeOrderSn).Bytes(),
			}); err != nil {
				g.Log().Error(ctx, "发送过期自动取消订单MQ失败", err)
			}
			res.TransactionSn = TransactionSn
			res.Countdown = ExpirationTime - gvar.New(gtime.Now().Unix()).Int()
		} else {
			if _, err = dao.PmsAppReservationChange.Ctx(ctx).TX(tx).Where(g.MapStrAny{
				dao.PmsAppReservationChange.Columns().ChangeOrderSn: req.ChangeOrderSn,
			}).Data(g.MapStrAny{
				dao.PmsAppReservationChange.Columns().ChangeStatus: "DONE",
				dao.PmsAppReservationChange.Columns().DoneDate:     gtime.Now().Format("Y-m-d H:i:s"),
			}).Update(); err != nil {
				return
			}
			if _, err = dao.PmsAppReservation.Ctx(ctx).TX(tx).Where(g.MapStrAny{
				dao.PmsAppReservation.Columns().Id: PmsAppReservationChange.OrderId,
			}).Data(g.MapStrAny{
				dao.PmsAppReservation.Columns().IsChangePeople:   "Y",
				dao.PmsAppReservation.Columns().IsChangePeopleId: PmsAppReservationChange.Id,
				dao.PmsAppReservation.Columns().AdultCount:       PmsAppReservationChange.NewAdultCount,
				dao.PmsAppReservation.Columns().ChildCount:       PmsAppReservationChange.NewChildCount,
				dao.PmsAppReservation.Columns().InfantCount:      PmsAppReservationChange.NewInfantCount,
			}).Update(); err != nil {
				return
			}

			// 酒店订单日志
			var (
				PmsAppStay entity.PmsAppStay
			)
			if err = dao.PmsAppStay.Ctx(ctx).Where(g.MapStrAny{
				dao.PmsAppStay.Columns().OrderSn: PmsAppReservationChange.OrderSn,
			}).Scan(&PmsAppStay); err != nil && errors.Is(err, sql.ErrNoRows) {
				return
			}
			if g.IsEmpty(PmsAppStay) {
				err = gerror.New("该订单无需处理")
				return
			}
			if _, err = dao.PmsAppStayLog.Ctx(ctx).OmitEmptyData().Insert(&entity.PmsAppStayLog{
				OrderId:     PmsAppStay.Id,
				ActionWay:   "CHANGE_PEOPLE_NUM",
				Remark:      gvar.New(PmsAppReservationChange.Id).String(),
				OperateType: "USER",
				OperateId:   PmsAppStay.MemberId,
			}); err != nil {
				return
			}
		}
		return
	}); err != nil {
		return
	}
	res.CreateOrderTime = PmsAppReservationChange.CreatedAt.Format("Y-m-d H:i:s")
	res.ChangeOrderSn = req.ChangeOrderSn
	res.ChangePrice = PmsAppReservationChange.ChangeAmount

	return
}

func (c *ControllerHotel) OrderChangeDatePreInfoSubmit(ctx context.Context, req *hotel.OrderChangeDatePreInfoSubmitReq) (res *hotel.OrderChangeDatePreInfoSubmitRes, err error) {
	var (
		PmsAppReservationChangeGvar *gvar.Var
		PmsAppReservationChange     *entity.PmsAppReservationChange
		PmsAppReservationChangeInfo *entity.PmsAppReservationChange
		PmsTransaction              []*entity.PmsTransaction
		TransactionSn               = uuid.CreatePayCode("PT")
		ChangeStatus                string
		DoneDate                    *gtime.Time
		ChangeDateId                int64
		PayConfig                   *model.PayConfig
		ExpirationTime              int
	)
	res = new(hotel.OrderChangeDatePreInfoSubmitRes)
	if PmsAppReservationChangeGvar, err = cache.Instance().Get(ctx, fmt.Sprintf("changeOrder:%s", req.ChangeOrderSn)); err != nil {
		return
	}
	if err = gvar.New(PmsAppReservationChangeGvar).Scan(&PmsAppReservationChange); err != nil {
		return
	}
	if g.IsEmpty(PmsAppReservationChange) {
		err = gerror.New("不存在变更单")
		return
	}
	// 查询变更单是否存在
	if err = dao.PmsAppReservationChange.Ctx(ctx).Where(g.MapStrAny{
		dao.PmsAppReservationChange.Columns().OrderId:      PmsAppReservationChange.OrderId,
		dao.PmsAppReservationChange.Columns().ChangeType:   "DATE",
		dao.PmsAppReservationChange.Columns().ChangeStatus: "DONE",
	}).Scan(&PmsAppReservationChangeInfo); err != nil {
		return
	}
	if !g.IsEmpty(PmsAppReservationChangeInfo) {
		err = gerror.New("已经进行过变更，无法继续变更")
		return
	}

	res.ChangeOrderSn = req.ChangeOrderSn
	res.DiffOrderPrice = PmsAppReservationChange.ChangeAmount
	if PayConfig, err = service.BasicsConfig().GetPay(ctx); err != nil {
		return
	}
	ExpirationTime = gvar.New(gtime.Now().Unix() + PayConfig.HotelStayExp).Int()
	if err = g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) (err error) {
		if res.DiffOrderPrice < 0 {
			// 写入入住变更单

			if ChangeDateId, err = dao.PmsAppReservationChange.Ctx(ctx).TX(tx).Data(&entity.PmsAppReservationChange{
				ChangeOrderSn:   req.ChangeOrderSn,
				OrderId:         PmsAppReservationChange.OrderId,
				OrderSn:         PmsAppReservationChange.OrderSn,
				OutOrderSn:      PmsAppReservationChange.OutOrderSn,
				OldCheckinDate:  gtime.New(PmsAppReservationChange.OldCheckinDate),
				OldCheckoutDate: gtime.New(PmsAppReservationChange.OldCheckoutDate),
				NewCheckinDate:  gtime.New(PmsAppReservationChange.NewCheckinDate),
				NewCheckoutDate: gtime.New(PmsAppReservationChange.NewCheckoutDate),
				ChangeType:      "DATE",
				ChangeStatus:    "DONE",
				ChangeAmount:    PmsAppReservationChange.ChangeAmount,
				SubmitDate:      gtime.Now(),
				DoneDate:        gtime.Now(),
				NewOrderPrice:   PmsAppReservationChange.NewOrderPrice,
				OldOrderPrice:   PmsAppReservationChange.OldOrderPrice,
				ExpirationTime:  ExpirationTime,
				PricePercent:    PmsAppReservationChange.PricePercent,
			}).InsertAndGetId(); err != nil {
				err = gerror.New("已经进行过变更，无法继续变更")
				return
			}

			// 回写住宿单变更信息
			if _, err = dao.PmsAppReservation.Ctx(ctx).TX(tx).Where(dao.PmsAppReservation.Columns().Id, PmsAppReservationChange.OrderId).Update(g.Map{
				dao.PmsAppReservation.Columns().IsChangeDate:   "Y",
				dao.PmsAppReservation.Columns().IsChangeDateId: ChangeDateId,
				dao.PmsAppReservation.Columns().CheckinDate:    PmsAppReservationChange.NewCheckinDate,
				dao.PmsAppReservation.Columns().CheckoutDate:   PmsAppReservationChange.NewCheckoutDate,
			}); err != nil {
				return
			}

			// 查询支付信息
			if err = dao.PmsTransaction.Ctx(ctx).TX(tx).Where(g.Map{
				dao.PmsTransaction.Columns().OrderSn:   PmsAppReservationChange.OrderSn,
				dao.PmsTransaction.Columns().PayStatus: "DONE",
			}).WhereNot(dao.PmsTransaction.Columns().RefundStatus, "DONE").
				OrderDesc(dao.PmsTransaction.Columns().Id).Scan(&PmsTransaction); err != nil {
				return
			}

			if len(PmsTransaction) == 0 {
				return
			}

			if err = service.Refund().RefundOrder(ctx, &input_refund.RefundAmountInp{
				OrderSn:      PmsAppReservationChange.OrderSn,
				RefundAmount: math.Abs(PmsAppReservationChange.ChangeAmount),
			}, tx); err != nil {
				return
			}

			// 酒店订单日志
			var (
				PmsAppStay entity.PmsAppStay
			)
			if err = dao.PmsAppStay.Ctx(ctx).Where(g.MapStrAny{
				dao.PmsAppStay.Columns().OrderSn: PmsAppReservationChange.OrderSn,
			}).Scan(&PmsAppStay); err != nil && errors.Is(err, sql.ErrNoRows) {
				return
			}
			if g.IsEmpty(PmsAppStay) {
				err = gerror.New("该订单无需处理")
				return
			}

			if _, err = dao.PmsAppStayLog.Ctx(ctx).OmitEmptyData().Insert(&entity.PmsAppStayLog{
				OrderId:     PmsAppStay.Id,
				ActionWay:   "REFUND",
				Remark:      "订单退款",
				OperateType: "USER",
				OperateId:   PmsAppStay.MemberId,
			}); err != nil {
				return
			}

			if _, err = dao.PmsAppStayLog.Ctx(ctx).OmitEmptyData().Insert(&entity.PmsAppStayLog{
				OrderId:     PmsAppStay.Id,
				ActionWay:   "CHANGE_DATE",
				Remark:      gvar.New(ChangeDateId).String(),
				OperateType: "USER",
				OperateId:   PmsAppStay.MemberId,
			}); err != nil {
				return
			}
		} else {
			ChangeStatus = "ING"
			DoneDate = gtime.Now()
			if PmsAppReservationChange.ChangeAmount == 0 {
				ChangeStatus = "DONE"
				DoneDate = nil
			}
			if ChangeDateId, err = dao.PmsAppReservationChange.Ctx(ctx).TX(tx).Data(&entity.PmsAppReservationChange{
				ChangeOrderSn:   req.ChangeOrderSn,
				OrderId:         PmsAppReservationChange.OrderId,
				OrderSn:         PmsAppReservationChange.OrderSn,
				OutOrderSn:      PmsAppReservationChange.OutOrderSn,
				OldCheckinDate:  gtime.New(PmsAppReservationChange.OldCheckinDate),
				OldCheckoutDate: gtime.New(PmsAppReservationChange.OldCheckoutDate),
				NewCheckinDate:  gtime.New(PmsAppReservationChange.NewCheckinDate),
				NewCheckoutDate: gtime.New(PmsAppReservationChange.NewCheckoutDate),
				ChangeType:      "DATE",
				ChangeStatus:    ChangeStatus,
				ChangeAmount:    PmsAppReservationChange.ChangeAmount,
				SubmitDate:      gtime.Now(),
				DoneDate:        DoneDate,
				ExpirationTime:  ExpirationTime,
				OldOrderPrice:   PmsAppReservationChange.OldOrderPrice,
				NewOrderPrice:   PmsAppReservationChange.NewOrderPrice,
			}).InsertAndGetId(); err != nil {
				err = gerror.New("已经进行过变更，无法继续变更")
				return
			}

			if PmsAppReservationChange.ChangeAmount == 0 {
				if _, err = dao.PmsAppReservation.Ctx(ctx).Where(g.Map{
					dao.PmsAppReservation.Columns().Id: PmsAppReservationChange.OrderId,
				}).Data(g.Map{
					dao.PmsAppReservation.Columns().IsChangeDate:   "Y",
					dao.PmsAppReservation.Columns().IsChangeDateId: ChangeDateId,
				}).Update(); err != nil {
					return
				}

				// 酒店订单日志
				var (
					PmsAppStay entity.PmsAppStay
				)
				if err = dao.PmsAppStay.Ctx(ctx).Where(g.MapStrAny{
					dao.PmsAppStay.Columns().OrderSn: PmsAppReservationChange.OrderSn,
				}).Scan(&PmsAppStay); err != nil && errors.Is(err, sql.ErrNoRows) {
					return
				}
				if g.IsEmpty(PmsAppStay) {
					err = gerror.New("该订单无需处理")
					return
				}
				if _, err = dao.PmsAppStayLog.Ctx(ctx).OmitEmptyData().Insert(&entity.PmsAppStayLog{
					OrderId:     PmsAppStay.Id,
					ActionWay:   "CHANGE_DATE",
					Remark:      gvar.New(ChangeDateId).String(),
					OperateType: "USER",
					OperateId:   PmsAppStay.MemberId,
				}); err != nil {
					return
				}

				return
			}
			// 插入支付订单流水
			if _, err = dao.PmsTransaction.Ctx(ctx).TX(tx).OmitEmptyData().Data(&entity.PmsTransaction{
				OrderSn:       PmsAppReservationChange.OrderSn,
				ChangeOrderSn: req.ChangeOrderSn,
				TransactionSn: TransactionSn,
				PayChannel:    "PAYCLOUD",
				PayType:       "WeChatPay",
				OrderType:     "BOOKING_CHANGE",
				Scene:         "HOTEL",
				Amount:        PmsAppReservationChange.ChangeAmount,
				PriceCurrency: "JPY",
				PayStatus:     "WAIT",
				ExpiredTime:   gtime.New(ExpirationTime),
			}).InsertAndGetId(); err != nil {
				return
			}
			res.TransactionSn = TransactionSn
			res.CreateOrderTime = gtime.Now().Format("Y-m-d H:i:s")
			res.Countdown = PmsAppReservationChange.ExpirationTime - gvar.New(gtime.Now().Unix()).Int()
			if err = rabbitmq.SendMqMessage(ctx, &rabbitmq.SendMqMessageParams{
				ExchangeName: consts.RabbitMQExchangeDelayedName,
				QueueName:    consts.RabbitMQQueueNameOrderExpire,
				DataByte:     gvar.New(req.ChangeOrderSn).Bytes(),
			}); err != nil {
				g.Log().Error(ctx, "发送过期自动取消订单MQ失败", err)
			}
		}
		return
	}); err != nil {
		return
	}
	return
}

func (c *ControllerHotel) OrderChangeBookingPeopleInfo(ctx context.Context, req *hotel.OrderChangeBookingPeopleInfoReq) (res *hotel.OrderChangeBookingPeopleInfoRes, err error) {
	var (
		AppReservation *entity.PmsAppReservation
		RoomType       *entity.PmsRoomType
	)
	res = new(hotel.OrderChangeBookingPeopleInfoRes)
	if err = dao.PmsAppReservation.Ctx(ctx).WherePri(req.OrderId).WithAll().Scan(&AppReservation); err != nil {
		return
	}
	res.Child = AppReservation.ChildCount
	res.Adult = AppReservation.AdultCount
	res.Infant = AppReservation.InfantCount

	if err = dao.PmsRoomType.Ctx(ctx).
		Where(dao.PmsRoomType.Columns().Uid, AppReservation.RoomType).
		Scan(&RoomType); err != nil {
		return
	}

	if g.IsEmpty(RoomType) {
		err = gerror.New("暂不支持变更入住人信息")
		return
	}

	res.Occupancy = RoomType.Occupancy

	return
}

func (c *ControllerHotel) OrderChangeStayOnInfo(ctx context.Context, req *hotel.OrderChangeStayOnInfoReq) (res *hotel.OrderChangeStayOnInfoRes, err error) {
	var (
		AppReservation      *entity.PmsAppReservation
		PmsAvailabilities   *entity.PmsAvailabilities
		RoomType            *entity.PmsRoomType
		PreCreateOrderInp   = new(input_hotel.PreCreateOrderInp)
		PreCreateOrderModel = new(input_hotel.PreCreateOrderModel)
		StartDate           *gtime.Time
		EndDate             *gtime.Time
		RoomItems           *input_hotel.RoomItems
	)
	res = new(hotel.OrderChangeStayOnInfoRes)
	if err = dao.PmsAppReservation.Ctx(ctx).WherePri(req.OrderId).WithAll().Scan(&AppReservation); err != nil {
		return
	}
	if g.IsEmpty(AppReservation) {
		err = gerror.New("不存在入住单")
		return
	}
	if err = dao.PmsRoomType.Ctx(ctx).
		Where(dao.PmsRoomType.Columns().Uid, AppReservation.RoomType).
		Hook(hook.PmsFindLanguageValueHook).
		Scan(&RoomType); err != nil {
		return
	}
	if g.IsEmpty(RoomType) {
		err = gerror.New("不存在房型")
		return
	}

	res.Cover = RoomType.Cover
	res.Name = RoomType.Name
	StartDate = AppReservation.CheckoutDate
	EndDate = StartDate.Add(time.Hour * 24 * time.Duration(req.NightNum))
	res.CheckOutDate = EndDate.Format("Y-m-d")

	// 批量查询物业在时间段中的最低价格
	if err = dao.PmsAvailabilities.Ctx(ctx).
		Fields(dao.PmsAvailabilities.Columns().Tuid, "min(price) as price", "min(allotment) as allotment").
		Where(dao.PmsAvailabilities.Columns().Tuid, AppReservation.RoomType).
		WhereGT(dao.PmsAvailabilities.Columns().Price, 0).
		WhereGTE(dao.PmsAvailabilities.Columns().Date, StartDate).
		WhereLT(dao.PmsAvailabilities.Columns().Date, EndDate).
		Group(dao.PmsAvailabilities.Columns().Tuid).Scan(&PmsAvailabilities); err != nil && !errors.Is(err, sql.ErrNoRows) {
		return
	}
	if g.IsEmpty(PmsAvailabilities) {
		err = gerror.New("暂不支持续住")
		return
	}

	if PmsAvailabilities.Allotment < 1 {
		err = gerror.NewCode(gcode.New(1111, "您选择的日期已经没有满足条件的房间库存了", nil))
		return
	}

	res.BasePrice = PmsAvailabilities.Price
	res.Inventory = PmsAvailabilities.Allotment

	PreCreateOrderInp.Puid = AppReservation.Puid
	RoomItems = new(input_hotel.RoomItems)
	RoomItems = &input_hotel.RoomItems{
		RoomId:     AppReservation.RoomType,
		RoomNoId:   AppReservation.RoomUnit,
		RoomNum:    1,
		Adult:      AppReservation.AdultCount,
		Child:      AppReservation.ChildCount,
		Infant:     AppReservation.InfantCount,
		BookingFee: 0,
		Remark:     "",
	}
	PreCreateOrderInp = &input_hotel.PreCreateOrderInp{
		Puid:       AppReservation.Puid,
		CheckinAt:  StartDate.Format("Y-m-d"),
		CheckoutAt: EndDate.Format("Y-m-d"),
		RoomItems:  []*input_hotel.RoomItems{RoomItems},
	}
	if PreCreateOrderModel, err = service.HotelService().PreOrder(ctx, PreCreateOrderInp); err != nil {
		return
	}
	res.PreOrderSn = PreCreateOrderModel.PreOrderSn
	// 查询最长可续住天数
	PmsAvailabilities = nil
	if err = dao.PmsAvailabilities.Ctx(ctx).
		Where(dao.PmsAvailabilities.Columns().Tuid, AppReservation.RoomType).
		WhereGTE(dao.PmsAvailabilities.Columns().Date, StartDate).
		Where(dao.PmsAvailabilities.Columns().Allotment, 1).
		Scan(&PmsAvailabilities); err != nil {
		return
	}
	if g.IsEmpty(PmsAvailabilities) {
		if err = dao.PmsAvailabilities.Ctx(ctx).
			Where(dao.PmsAvailabilities.Columns().Tuid, AppReservation.RoomType).
			WhereGTE(dao.PmsAvailabilities.Columns().Date, StartDate).
			OrderDesc(dao.PmsAvailabilities.Columns().Date).
			Scan(&PmsAvailabilities); err != nil {
			return
		}
		if g.IsEmpty(PmsAvailabilities) {
			err = gerror.New("暂不支持续住")
			return
		}
		return
	}
	res.MaxStayNoDays = convert.Diff(StartDate.Format("Y-m-d"), PmsAvailabilities.Date, "day")
	return
}

func (c *ControllerHotel) OrderChangeInfoDetail(ctx context.Context, req *hotel.OrderChangeInfoDetailReq) (res *hotel.OrderChangeInfoDetailRes, err error) {
	res = new(hotel.OrderChangeInfoDetailRes)

	if err = dao.PmsAppReservationChange.Ctx(ctx).Where(g.MapStrAny{
		dao.PmsAppReservationChange.Columns().ChangeOrderSn: req.ChangeOrderSn,
	}).Scan(&res); err != nil {
		return
	}
	res.Countdown = res.ExpirationTime - gvar.New(gtime.Now().Unix()).Int()
	return
}
